<?php

namespace App\Service\SearchService\Index;

use App\Service\SearchService\BaseElasticSearchClientService;
use App\Service\Code\Normal;
use App\Service\AppService\SystemCacheConfigService;

/**
 * 商城商品搜索
 * @author chenshubo
 */
class ShopCommodityIndex extends BaseElasticSearchClientService
{
    const SHOP_COMMODITY_CHANGE     = 'ShopCommodityChange';
    const SHOP_COMMODITY_DEL        = 'ShopCommodityDel';
    const SHOP_COMMODITY_ALL_DEL    = 'ShopCommodityAllDel';
    
    //消息变化类型
    protected $change_map = [
        self::SHOP_COMMODITY_CHANGE     => '商城商品变更',
        self::SHOP_COMMODITY_DEL        => '商城商品下架',
        self::SHOP_COMMODITY_ALL_DEL    => '商城商品全部下架'
    ];
    
    //索引名字
    public $index = 'yc_shop_commodity';
    
    /**
     * 商品索引结构
     * @var array
     */
    public $mapping_property = [
        'SkuID' => [
            'type'  => 'float',
        ],
        'ID' => [
            'type'  => 'keyword',
        ],
        'CommodityType' => [
            'type'  => 'integer',
        ],
        'FirstClassificationID' => [
            'type'  => 'keyword',
        ],
        'SecondClassificationID' => [
            'type'  => 'keyword',
        ],
        'ThirdClassificationID' => [
            'type'  => 'keyword',
        ],
        'BrandID' => [
            'type'  => 'keyword',
        ],
        'HasTaxCostPrice' => [
            'type'  => 'float',
        ],
        'GoodsName' => [
            'type'      => 'text',
            'analyzer'  => 'ik_smart',
            'search_analyzer'   => 'ik_smart',
            'fields'    => [
                "keyword"=> [
                    "type"              => "text",
                    "analyzer"          => "ik_pinyin_analyzer",
                    "search_analyzer"   => "ik_pinyin_analyzer",
                    "index"             => true,
                    "store"             => true
                ],
                "suggest"=> [
                    "type"                          => "completion",
                    "analyzer"                      => "simple",
                    "preserve_separators"           => true,
                    "preserve_position_increments"  => true,
                    "max_input_length"              => 50
                ]
            ]
        ],
    ];
    
    /**
     * 批量处理文档
     */
    public function addMultiDoc( $param = [] )
    {
        $DataList = $param['DataList'];
        $MessageType = $param['MessageType'];
        if( empty($param['MessageType']) ) {
            return Normal::returnCode(Normal::FAIL,"消息类型MessageType不存在");
        }
        
        if( empty($DataList) ) {
            return Normal::returnCode(Normal::SUC,"数据为空，无需要消费");
        }
        
        if( !isset($param['DataList']) ) {
            return Normal::returnCode(Normal::FAIL,"消息DataList不存在");
        }
        
        if( !isset($this->change_map[$MessageType]) ) {
            return Normal::returnCode(Normal::FAIL,"消息类型{$MessageType}不存在");
        }
        
        $MessageType = $param['MessageType'];
        if( $MessageType == self::SHOP_COMMODITY_ALL_DEL ) {//全部下架ShopCommodityAllDel
            $param = [
                'index' => $this->index,
                'body' => ['query' => ['match_all' => new \stdClass()]]
            ];
            return $this->deleteByQuery($param);
        } else if( $MessageType == self::SHOP_COMMODITY_DEL ) {//如果是批量下架ShopCommodityDel 
            $body = [
                'index' => $this->index,
                'body' => ['query' => ['bool' =>['filter'=>[['terms'=>['ID'=>$DataList]]]]]],
            ];
            return $this->deleteByQuery($body);
        } else if( $MessageType == self::SHOP_COMMODITY_CHANGE ) {//下面是增量更新的数据ShopCommodityChange
            
            $SkuMapList     = array_column($DataList,null,'SkuID');
            $SkuIDs         = array_keys($SkuMapList);
            $limit          = count($SkuIDs);
            $searchResult   = $this->search(0, $limit,['skuIDS'=>$SkuIDs]);
            $hasSkuList     = $searchResult['hits']['hits'];
            
            //如果有存在的数据，则剔除
            $body = [];
            if( $hasSkuList )
            {
                foreach ($hasSkuList as $skuInfo )
                {
                    $body[] = [
                        'delete' => [
                            '_index'    => $this->index,
                            '_id'       => $skuInfo['_id']
                        ]
                    ];
                }
            }
            
            //新增写入
            $body = [];
            foreach ($DataList as $item )
            {
                $SkuID = $item['SkuID'];
                $body[] = [
                    'index' => [
                        '_index'    => $this->index,
                        '_id'       => $SkuID
                    ]
                ];
                $body[] = $item;
            }
            return $this->bulk($body);
        } else {
            return Normal::returnCode(Normal::FAIL,"未找到定义模块【{$MessageType}】");
        }
    }
    
    /**
     * 搜索商品
     * @return array
     */
    public function search($page,$limit,$param)
    {
        $query = $this->buildElasticSearch($param);
        if( empty($query) ) {
            $query['match_all'] =  new \stdClass();
        }
        
        //价格排序
        $sortRule = [];
        
        //默认保税排序在前面，一般贸易和直邮随机(自定义排序类型painless，params.field must be string ,can't be other mode data given)
        /* $sort_config = SystemCacheConfigService::instance()->getCacheInfoBySystemCode('shop_sort_rule');
        $sortRule['_script']['type']    = 'number';      
        $sortRule['_script']['script']  = [
            'source'    => "return params[doc['CommodityType'].value]",
            'lang'      => 'painless',
            'params'    => [
                '1' => $sort_config['shop_sort_rule']['1'] ?? 300,
                '0' => $sort_config['shop_sort_rule']['0'] ?? 200,
                '2' => $sort_config['shop_sort_rule']['2'] ?? 200,
            ]
        ];      
        $sortRule['_script']['order']   = 'desc'; */
        
        if( isset($param['sort']) && is_array($param['sort']) ) {
            $sort = $param['sort'];
            if( isset($sort['price']) && !empty($sort['price']) ) {
                $sortRule[]['HasTaxCostPrice']['order'] = $sort['price'] == 'asc' ? 'asc' :'desc';
            } else {
                $sortRule[]['HasTaxCostPrice']['order'] = 'asc';
            }
        } else {
            $sortRule[]['HasTaxCostPrice']['order'] = 'asc';
        }
        
        //ID正序
        $sortRule[]['SkuID']['order'] = 'asc';
        $request = [
            'index'     => $this->index,
            'body'      => [
                'query'     => $query,
                'size'      => $limit,
                'from'      => $page * $limit
            ],
        ];
        
        if( !empty($sortRule) ) {
           $request['body']['sort'] = $sortRule;
        }
        return $this->getAggsList($request);
    }
    
    /**
     * 构建搜索参数
     */
    public function buildElasticSearch( $param = [] )
    {
        //搜索商品名称
        $body = [];
        if( !empty($param['keyword']) )
        {
            $keyword = $param['keyword'];
			$body['bool']['must'][] = ['match'=>['GoodsName'=>[
				'query'     => $keyword,
				'operator'  => 'and'
			]]];            
            //优先拆分词组
            /**$analyze_tokens = $this->analyze($keyword);
            if( empty($analyze_tokens['tokens']) ) {
                $body['bool']['must'][] = ['match'=>['GoodsName'=>[
                    'query'     => $keyword,
                    'operator'  => 'and',
                    'minimum_should_match'  => '50%',
                    'boost'     => 4,
                ]]];
                $body['bool']['should'][] = ['match'=>['GoodsName'=>[
                    'query'     => $keyword,
                    'boost'     => 3,
                ]]];
                $body['bool']['should'][] = ['match_phrase'=>['GoodsName'=>[
                    'query'     => $keyword,
                    'boost'     => 2,
                ]]];
                $body['bool']['should'][] = ['match_phrase_prefix'=>['GoodsName'=>[
                    'query'     => $keyword,
                    'boost'     => 1,
                ]]];
            } else {
                
                $queries = [];
                foreach ($analyze_tokens['tokens'] as $item ) 
                {
                    $queries[] = [
                        'match' => ['GoodsName'=>$item['token']]
                    ];
                }
                
                $body['dis_max'] = [
                    'tie_breaker'   => 0.5,
                    'boost'         => 1.2,
                    'queries'       => $queries
                ];
            }**/
        }
        
        //一级分类
        if(!empty($param['f_class_id']) ) {
            $body['bool']['must'][] = ['match'=>['FirstClassificationID'=>$param['f_class_id']]];
        }
        
        //二级分类
        if(!empty($param['s_class_id']) ) {
            $body['bool']['must'][] = ['match'=>['SecondClassificationID'=>$param['s_class_id']]];
        }
        
        //三级分类
        if(!empty($param['t_class_id']) ) {
            $body['bool']['must'][] = ['match'=>['ThirdClassificationID'=>$param['t_class_id']]];
        }
        
        //品牌IDS
        if( isset($param['brand_ids']) && !empty($param['brand_ids']) ) {
            $body['bool']['must'][] = ['terms'=>['BrandID'=>$param['brand_ids']]];
        }
        
        //商品分类
        if( isset($param['goods_types']) && !empty($param['goods_types']) ) {
            $body['bool']['must'][] = ['terms'=>['CommodityType'=>$param['goods_types']]];
        }
        
        //分类IDS
        if( isset($param['class_ids']) && !empty($param['class_ids']) ) {
            $body['bool']['filter']['bool']['should'][] = ['terms'=>['FirstClassificationID'=>$param['class_ids']]];
            $body['bool']['filter']['bool']['should'][] = ['terms'=>['SecondClassificationID'=>$param['class_ids']]];
            $body['bool']['filter']['bool']['should'][] = ['terms'=>['ThirdClassificationID'=>$param['class_ids']]];
        }
        
        //SkuID
        if( isset($param['skuIDS']) && !empty($param['skuIDS']) ) {
            $body['bool']['filter'][] = ['terms'=>['ID'=>$param['skuIDS']]];
        }
        
        //如果没有任何搜索条件
        if( empty($body) ) {
            $body['match_all'] = new \stdClass();
        }
        
        return $body;
    }
    
    /**
     * 获取搜索分组对应品牌，分类、商品类型数据
     * @param array $param
     */
    public function getSearchClassAndBrands( $param = [] )
    {
        $aggs = [];
        $aggs['f_class_id']['terms']['field']   = 'FirstClassificationID';
        $aggs['s_class_id']['terms']['field']   = 'SecondClassificationID';
        $aggs['t_class_id']['terms']['field']   = 'ThirdClassificationID';
        $aggs['goods_type']['terms']['field']   = 'CommodityType';
        $aggs['brand_id']['terms']['field']     = 'BrandID';
        $search = $this->buildElasticSearch($param);
        $body = [
            'query' => $search,
            'aggs' => $aggs,
        ];
        $request = [
            'index' => $this->index,
            'body'  => $body,
            'size'  => 0,
        ];
        return $this->getAggsList($request);
    }
    
    /**
     * 菜单分类树
     */
    public function getClassList()
    {
        $body = [];
        $body['index'] = $this->index;
        $param['size']  = 0;
        //将size设置为全量值，来解决精度问题  (-2147483648 - 2147483647)
        //问题链接：https://blog.csdn.net/weixin_48726650/article/details/107139023
        $maxSize = 2147483647;
        // $param['aggs']['f_class_id']['terms']['field'] = 'FirstClassificationID';
        $param['aggs']['f_class_id']['terms'] = [
            'field' => 'FirstClassificationID',
            'size'  => $maxSize
        ];
        // $param['aggs']['s_class_id']['terms']['field'] = 'SecondClassificationID';
        $param['aggs']['s_class_id']['terms'] = [
            'field' => 'SecondClassificationID',
            'size'  => $maxSize
        ];
        // $param['aggs']['t_class_id']['terms']['field'] = 'ThirdClassificationID';
        $param['aggs']['t_class_id']['terms'] = [
            'field' => 'ThirdClassificationID',
            'size'  => $maxSize
        ];
        $body['body'] = $param;
        return $this->getAggsList($body);
    }
    
    /**
     * 创建文档索引
     */
    public function putMapping( $param = [] )
    {
        $isExists = $this->existsIndices();
        if( !$isExists ) {
            return $this->createMapping();
        } else {
            return Normal::returnCode(Normal::SUC,"索引已存在",$this->getMapping());
        }
    }
}